home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / IFC_112 / netscape / util / ASCIIArchiveLoader.java < prev    next >
Encoding:
Text File  |  1999-05-28  |  32.0 KB  |  923 lines  |  [TEXT/CWIE]

  1. // ASCIIArchiveLoader.java
  2. // By Ned Etcode
  3. // Copyright 1995, 1996, 1997 Netscape Communications Corp.  All rights reserved.
  4.  
  5. package netscape.util;
  6.  
  7. import java.io.*;
  8.  
  9. class ASCIIArchiveLoader {
  10.     static final String classTablesKey = "classTables";
  11.     static final String fieldNamesKey = "fieldNames";
  12.     static final String fieldTypesKey = "fieldTypes";
  13.     static final String classNamesKey = "classNames";
  14.     static final String classVersionsKey = "classVersions";
  15.     static final String instancesKey = "instances";
  16.     static final String rootInstancesKey = "rootInstances";
  17.     static final String archiveVersionKey = "archiveVersion";
  18.  
  19.     Archive archive;
  20.     Hashtable archiveDict;
  21.  
  22.     IdHashtable idForName;
  23.     String nameForId[];
  24.     Hashtable baseNameForTable;
  25.  
  26.     ASCIIArchiveLoader(Archive archive) {
  27.         super();
  28.         this.archive = archive;
  29.     }
  30.  
  31.     void readASCII(InputStream in) throws CodingException,
  32.         DeserializationException, IOException {
  33.         Deserializer deserializer = null;
  34.         String versionString;
  35.  
  36.         if (in instanceof Deserializer) {
  37.             deserializer = (Deserializer) in;
  38.         }
  39.  
  40.         // While reading an ASCII archive we need to maintain a mapping
  41.         // between instance "names" in the ASCII file, and the archive ids for
  42.         // those names.
  43.  
  44.         idForName = new IdHashtable(true);
  45.  
  46.         // Read in the ASCII archive file.  If there are any syntax errors in
  47.         // the file this will throw a DeserializationException.  We can't
  48.         // really add more useful error information to that at this point.
  49.  
  50.         if( deserializer == null )
  51.             deserializer = new Deserializer(in);
  52.         archiveDict = (Hashtable)deserializer.readObject();
  53.  
  54.         // Check the version information.
  55.  
  56.         versionString = (String)archiveDict.get(archiveVersionKey);
  57.         if (versionString == null)
  58.             throw new IOException("Missing archiveVersion");
  59.  
  60.         archive.version = Integer.parseInt(versionString);
  61.         if (archive.version != Archive.ARCHIVE_VERSION)
  62.             throw new IOException("Unknown archiveVersion " + archive.version);
  63.  
  64.         // Load the pieces of the archive file.
  65.  
  66.         loadClassInfo();
  67.         loadInstanceData();
  68.         loadRoots();
  69.     }
  70.  
  71.     void loadClassInfo() throws CodingException {
  72.         Hashtable allTablesDict, tableDict;
  73.         Enumeration classNameEnum;
  74.         String className;
  75.         Object fieldNames[], fieldTypes[], classNames[], classVersions[];
  76.         ClassTable table;
  77.         ClassInfo info;
  78.         int i, version;
  79.  
  80.         // Get the information for each of the class tables.  It is not
  81.         // immediately an error to have no class tables, but if there are
  82.         // instances in the archive they will have no place to go and an
  83.         // exception will be thrown during loadInstanceData().
  84.  
  85.         allTablesDict = (Hashtable)archiveDict.get(classTablesKey);
  86.         if (allTablesDict == null)
  87.             return;
  88.  
  89.         classNameEnum = allTablesDict.keys();
  90.  
  91.         while (classNameEnum.hasMoreElements()) {
  92.  
  93.             className = (String)classNameEnum.nextElement();
  94.             tableDict = (Hashtable)allTablesDict.get(className);
  95.             info = new ClassInfo(className);
  96.  
  97.             classNames = (Object[])tableDict.get(classNamesKey);
  98.             classVersions = (Object[])tableDict.get(classVersionsKey);
  99.  
  100.             for (i = 0; i < classNames.length; i++) {
  101.                 version = Integer.parseInt((String)classVersions[i]);
  102.                 info.addClass((String)classNames[i], version);
  103.             }
  104.  
  105.             fieldNames = (Object[])tableDict.get(fieldNamesKey);
  106.             fieldTypes = (Object[])tableDict.get(fieldTypesKey);
  107.  
  108.             for (i = 0; i < fieldNames.length; i++) {
  109.                 info.addField((String)fieldNames[i],
  110.                     typeForName((String)fieldTypes[i]));
  111.             }
  112.  
  113.             table = new ClassTable(archive, info);
  114.             archive.addClassTable(table);
  115.         }
  116.     }
  117.  
  118.     static byte typeForName(String typeName) throws CodingException {
  119.         int length;
  120.  
  121.         length = typeName.length();
  122.  
  123.         if (length <= 0)
  124.             throw new CodingException("unknown type name: " + typeName);
  125.  
  126.         switch(typeName.charAt(0)) {
  127.             case 'b':
  128.                 if (typeName.equals("boolean"))
  129.                     return Codable.BOOLEAN_TYPE;
  130.                 if (typeName.equals("boolean[]"))
  131.                     return Codable.BOOLEAN_ARRAY_TYPE;
  132.                 if (typeName.equals("byte"))
  133.                     return Codable.BYTE_TYPE;
  134.                 if (typeName.equals("byte[]"))
  135.                     return Codable.BYTE_ARRAY_TYPE;
  136.                 break;
  137.             case 'c':
  138.                 if (typeName.equals("char"))
  139.                     return Codable.CHAR_TYPE;
  140.                 if (typeName.equals("char[]"))
  141.                     return Codable.CHAR_ARRAY_TYPE;
  142.                 break;
  143.             case 's':
  144.                 if (typeName.equals("short"))
  145.                     return Codable.SHORT_TYPE;
  146.                 if (typeName.equals("short[]"))
  147.                     return Codable.SHORT_ARRAY_TYPE;
  148.                 break;
  149.             case 'i':
  150.                 if (typeName.equals("int"))
  151.                     return Codable.INT_TYPE;
  152.                 if (typeName.equals("int[]"))
  153.                     return Codable.INT_ARRAY_TYPE;
  154.                 break;
  155.             case 'l':
  156.                 if (typeName.equals("long"))
  157.                     return Codable.LONG_TYPE;
  158.                 if (typeName.equals("long[]"))
  159.                     return Codable.LONG_ARRAY_TYPE;
  160.                 break;
  161.             case 'f':
  162.                 if (typeName.equals("float"))
  163.                     return Codable.FLOAT_TYPE;
  164.                 if (typeName.equals("float[]"))
  165.                     return Codable.FLOAT_ARRAY_TYPE;
  166.                 break;
  167.             case 'd':
  168.                 if (typeName.equals("double"))
  169.                     return Codable.DOUBLE_TYPE;
  170.                 if (typeName.equals("double[]"))
  171.                     return Codable.DOUBLE_ARRAY_TYPE;
  172.                 break;
  173.             case 'j':
  174.                 if (length == 16) {
  175.                     if (typeName.equals("java.lang.String"))
  176.                         return Codable.STRING_TYPE;
  177.                     if (typeName.equals("java.lang.Object"))
  178.                         return Codable.OBJECT_TYPE;
  179.                 } else if (length == 18) {
  180.                     if (typeName.equals("java.lang.String[]"))
  181.                         return Codable.STRING_ARRAY_TYPE;
  182.                     if (typeName.equals("java.lang.Object[]"))
  183.                         return Codable.OBJECT_ARRAY_TYPE;
  184.                 }
  185.                 break;
  186.         }
  187.  
  188.         throw new CodingException("unknown type name: " + typeName);
  189.     }
  190.  
  191.     void loadInstanceData() throws CodingException {
  192.         String className;
  193.         Enumeration classNameEnum;
  194.         Hashtable allTablesDict, tableDict;
  195.  
  196.         allTablesDict = (Hashtable)archiveDict.get(classTablesKey);
  197.         if (allTablesDict == null)
  198.             return;
  199.  
  200.         // In the ASCII version of the archive we need to make two passes over
  201.         // the instances.  The first pass creates a row for each instance and
  202.         // builds the name to id mapping.
  203.  
  204.         classNameEnum = allTablesDict.keys();
  205.  
  206.         while (classNameEnum.hasMoreElements()) {
  207.             className = (String)classNameEnum.nextElement();
  208.             tableDict = (Hashtable)allTablesDict.get(className);
  209.  
  210.             loadRowMapping(className, tableDict);
  211.         }
  212.  
  213.         // The second pass over the instances actually loads the data for each
  214.         // instance into the row.
  215.  
  216.         classNameEnum = allTablesDict.keys();
  217.  
  218.         while (classNameEnum.hasMoreElements()) {
  219.             className = (String)classNameEnum.nextElement();
  220.             tableDict = (Hashtable)allTablesDict.get(className);
  221.  
  222.             loadRowData(className, tableDict);
  223.         }
  224.  
  225.     }
  226.  
  227.     void loadRowMapping(String className, Hashtable tableDict)
  228.         throws CodingException {
  229.         int id;
  230.         Hashtable allInstancesDict, instanceDict;
  231.         ClassTable table;
  232.         Enumeration nameEnum;
  233.         String name;
  234.  
  235.         allInstancesDict = (Hashtable)tableDict.get(instancesKey);
  236.         if (allInstancesDict == null)
  237.             return;
  238.  
  239.         nameEnum = allInstancesDict.keys();
  240.         while (nameEnum.hasMoreElements()) {
  241.             name = (String)nameEnum.nextElement();
  242.             instanceDict = (Hashtable)allInstancesDict.get(name);
  243.  
  244.             if (idForName.get(name) != IdHashtable.NOT_FOUND)
  245.                 throw new CodingException("duplicate instance name: "+name);
  246.  
  247.             table = archive.classTableForName(className);
  248.             if (table == null)
  249.                 throw new CodingException("bad class name for instance: " +
  250.                     name);
  251.  
  252.             id = table.newIdentifier();
  253.             idForName.putKnownAbsent(name, id);
  254.         }
  255.     }
  256.  
  257.     void loadRowData(String className, Hashtable tableDict)
  258.         throws CodingException {
  259.         int id, row;
  260.         Enumeration nameEnum;
  261.         String name;
  262.         ClassTable table;
  263.         Hashtable allInstancesDict, instanceDict;
  264.  
  265.         allInstancesDict = (Hashtable)tableDict.get(instancesKey);
  266.         if (allInstancesDict == null)
  267.             return;
  268.  
  269.         nameEnum = allInstancesDict.keys();
  270.         while (nameEnum.hasMoreElements()) {
  271.             name = (String)nameEnum.nextElement();
  272.             id = idForName.get(name);
  273.             table = archive.tableForId[id];
  274.             row = archive.rowForId[id];
  275.             instanceDict = (Hashtable)allInstancesDict.get(name);
  276.             loadRow(table, row, instanceDict);
  277.         }
  278.     }
  279.  
  280.     void loadRow(ClassTable table, int row, Hashtable instanceDict)
  281.         throws CodingException {
  282.         int i;
  283.         Object value;
  284.  
  285.         for (i = 0; i < table.fieldCount; i++) {
  286.             value = instanceDict.get(table.fieldNames[i]);
  287.             if (value != null)
  288.                 setColumnFromValue(table, row, i, value);
  289.         }
  290.     }
  291.  
  292.     void setColumnFromValue(ClassTable table, int row, int column, Object v)
  293.         throws CodingException {
  294.         int i, count;
  295.         boolean booleanArray[];
  296.         char charArray[];
  297.         byte byteArray[];
  298.         short shortArray[];
  299.         int intArray[];
  300.         long longArray[];
  301.         float floatArray[];
  302.         double doubleArray[];
  303.         String stringArray[];
  304.         String value = null;
  305.         Object array[] = null;
  306.  
  307.         if (v instanceof String) {
  308.             value = (String)v;
  309.  
  310.             switch (table.fieldTypes[column]) {
  311.                 case Codable.BOOLEAN_TYPE:
  312.                     if (value.equalsIgnoreCase("true"))
  313.                         table.setBooleanAt(row, column, true);
  314.                     else if (value.equalsIgnoreCase("false"))
  315.                         table.setBooleanAt(row, column, false);
  316.                     else
  317.                         throw new CodingException("Invalid boolean value");
  318.                     break;
  319.                 case Codable.CHAR_TYPE:
  320.                     table.setCharAt(row, column, (char)value.charAt(0));
  321.                     break;
  322.                 case Codable.BYTE_TYPE:
  323.                     table.setByteAt(row, column,
  324.                         (byte)Integer.parseInt(value));
  325.                     break;
  326.                 case Codable.BYTE_ARRAY_TYPE:
  327.                     table.setByteArrayAt(row, column, bytesFromString(value));
  328.                     break;
  329.                 case Codable.SHORT_TYPE:
  330.                     table.setShortAt(row, column,
  331.                         (short)Integer.parseInt(value));
  332.                     break;
  333.                 case Codable.INT_TYPE:
  334.                     table.setIntAt(row, column, Integer.parseInt(value));
  335.                     break;
  336.                 case Codable.LONG_TYPE:
  337.                     table.setLongAt(row, column, Long.parseLong(value));
  338.                     break;
  339.                 case Codable.FLOAT_TYPE:
  340.                     table.setFloatAt(row, column,
  341.                         Float.valueOf(value).floatValue());
  342.                     break;
  343.                 case Codable.DOUBLE_TYPE:
  344.                     table.setDoubleAt(row, column,
  345.                         Double.valueOf(value).doubleValue());
  346.                     break;
  347.                 case Codable.STRING_TYPE:
  348.                     table.setStringAt(row, column, value);
  349.                     break;
  350.                 case Codable.OBJECT_TYPE:
  351.                     table.setIdentifierAt(row, column, idForName.get(value));
  352.                     break;
  353.                 default:
  354.                     throw new CodingException("Invalid value " + value);
  355.             }
  356.         } else if (v instanceof Object[]) {
  357.             array = (Object[])v;
  358.  
  359.             switch (table.fieldTypes[column]) {
  360.                 case Codable.BOOLEAN_ARRAY_TYPE:
  361.                     count = array.length;
  362.                     booleanArray = new boolean[count];
  363.                     for (i = 0; i < count; i++) {
  364.                         value = (String)array[i];
  365.                         if (value.equalsIgnoreCase("true"))
  366.                             booleanArray[i] = true;
  367.                         else if (value.equalsIgnoreCase("false"))
  368.                             booleanArray[i] = false;
  369.                         else
  370.                             throw new CodingException("Invalid boolean value");
  371.                     }
  372.                     table.setBooleanArrayAt(row, column, booleanArray);
  373.                     break;
  374.                 case Codable.CHAR_ARRAY_TYPE:
  375.                     count = array.length;
  376.                     charArray = new char[count];
  377.                     for (i = 0; i < count; i++) {
  378.                         value = (String)array[i];
  379.                         charArray[i] = value.charAt(0);
  380.                     }
  381.                     table.setCharArrayAt(row, column, charArray);
  382.                     break;
  383.                 case Codable.SHORT_ARRAY_TYPE:
  384.                     count = array.length;
  385.                     shortArray = new short[count];
  386.                     for (i = 0; i < count; i++) {
  387.                         value = (String)array[i];
  388.                         shortArray[i] = (short)Integer.parseInt(value);
  389.                     }
  390.                     table.setShortArrayAt(row, column, shortArray);
  391.                     break;
  392.                 case Codable.INT_ARRAY_TYPE:
  393.                     count = array.length;
  394.                     intArray = new int[count];
  395.                     for (i = 0; i < count; i++) {
  396.                         value = (String)array[i];
  397.                         intArray[i] = Integer.parseInt(value);
  398.                     }
  399.                     table.setIntArrayAt(row, column, intArray);
  400.                     break;
  401.                 case Codable.LONG_ARRAY_TYPE:
  402.                     count = array.length;
  403.                     longArray = new long[count];
  404.                     for (i = 0; i < count; i++) {
  405.                         value = (String)array[i];
  406.                         longArray[i] = Long.parseLong(value);
  407.                     }
  408.                     table.setLongArrayAt(row, column, longArray);
  409.                     break;
  410.                 case Codable.FLOAT_ARRAY_TYPE:
  411.                     count = array.length;
  412.                     floatArray = new float[count];
  413.                     for (i = 0; i < count; i++) {
  414.                         value = (String)array[i];
  415.                         floatArray[i] = Float.valueOf(value).floatValue();
  416.                     }
  417.                     table.setFloatArrayAt(row, column, floatArray);
  418.                     break;
  419.                 case Codable.DOUBLE_ARRAY_TYPE:
  420.                     count = array.length;
  421.                     doubleArray = new double[count];
  422.                     for (i = 0; i < count; i++) {
  423.                         value = (String)array[i];
  424.                         doubleArray[i] = Double.valueOf(value).doubleValue();
  425.                     }
  426.                     table.setDoubleArrayAt(row, column, doubleArray);
  427.                     break;
  428.                 case Codable.STRING_ARRAY_TYPE:
  429.                     count = array.length;
  430.                     stringArray = new String[count];
  431.                     for (i = 0; i < count; i++) {
  432.                         stringArray[i] = (String)array[i];
  433.                     }
  434.                     table.setStringArrayAt(row, column, stringArray);
  435.                     break;
  436.                 case Codable.OBJECT_ARRAY_TYPE:
  437.                     count = array.length;
  438.                     intArray = new int[count];
  439.                     for (i = 0; i < count; i++) {
  440.                         value = (String)array[i];
  441.                         if (value != null)
  442.                             intArray[i] = idForName.get(value);
  443.                     }
  444.                     table.setIdentifierArrayAt(row, column, intArray);
  445.                     break;
  446.                 default:
  447.                     throw new CodingException("Invalid value" + value);
  448.             }
  449.         }
  450.     }
  451.  
  452.     byte[] bytesFromString(String value) {
  453.         int i, count, nibble, outCount;
  454.         char ch;
  455.         byte buf[], tmp[];
  456.  
  457.         if (value == null)
  458.             return null;
  459.  
  460.         if (value.equals(""))
  461.             return new byte[0];
  462.  
  463.         count = value.length();
  464.         buf = new byte[count / 2 + 1];
  465.         i = 0;
  466.         outCount = 0;
  467.  
  468.         while (i < count) {
  469.             while (i < count) {
  470.                 ch = value.charAt(i++);
  471.                 if (Character.isSpace(ch))
  472.                     continue;
  473.  
  474.                 if (i >= count)
  475.                     throw new NumberFormatException("bad byte string");
  476.  
  477.                 nibble = nibbleForHexChar(ch);
  478.                 ch = value.charAt(i++);
  479.                 buf[outCount++] = (byte)((nibble << 4) + nibbleForHexChar(ch));
  480.             }
  481.         }
  482.  
  483.         tmp = new byte[outCount];
  484.         System.arraycopy(buf, 0, tmp, 0, outCount);
  485.  
  486.         return tmp;
  487.     }
  488.  
  489.     int nibbleForHexChar(char ch) {
  490.         if (ch >= '0' && ch <= '9')
  491.             return ch - '0';
  492.         else if (ch >= 'a' && ch <= 'f')
  493.             return ch - 'a' + 10;
  494.         else if (ch >= 'A' && ch <= 'F')
  495.             return ch - 'A' + 10;
  496.  
  497.         throw new NumberFormatException("bad byte string");
  498.     }
  499.  
  500.     void loadRoots() throws CodingException {
  501.         int i, id;
  502.         Object rootsArray[];
  503.         String name;
  504.  
  505.         rootsArray = (Object[])archiveDict.get(rootInstancesKey);
  506.  
  507.         if (rootsArray == null || rootsArray.length == 0)
  508.             return;
  509.  
  510.         for (i = 0; i < rootsArray.length; i++) {
  511.             name = (String)rootsArray[i];
  512.             if (name == null || name.equals(""))
  513.                 id = 0;
  514.             else
  515.                 id = idForName.get(name);
  516.  
  517.             if (id == 0)
  518.                 throw new CodingException("unknown root instance " + name);
  519.  
  520.             archive.addRootIdentifier(id);
  521.         }
  522.     }
  523.  
  524.     void writeASCII(OutputStream out, boolean formatted) throws IOException {
  525.         Serializer serializer;
  526.         String versionString;
  527.  
  528.         // We need to maintain a mapping from archive id to a human readable
  529.         // name for the ASCII file.  This is always accessed through the
  530.         // method nameForId().
  531.  
  532.         nameForId = new String[archive.idCount];
  533.         baseNameForTable = new Hashtable();
  534.  
  535.         // Save the pieces of the archive file.
  536.  
  537.         archiveDict = new Hashtable();
  538.  
  539.         versionString = String.valueOf(Archive.ARCHIVE_VERSION);
  540.         archiveDict.put(archiveVersionKey, versionString);
  541.  
  542.         saveClassInfo();
  543.         saveInstanceData();
  544.         saveRoots();
  545.  
  546.         // Write out the archive.
  547.  
  548.         if( formatted )
  549.             serializer = (Serializer) new FormattingSerializer(out);
  550.         else
  551.             serializer = new Serializer(out);
  552.  
  553.         serializer.writeObject(archiveDict);
  554.  
  555.         // Necessary. serializer has internal output buffer.
  556.         serializer.flush();
  557.     }
  558.  
  559.     String nameForId(int id) {
  560.         String name, base, seqString;
  561.         ClassTable table;
  562.         int lastDotIndex, digits;
  563.  
  564.         name = nameForId[id];
  565.  
  566.         if (name != null)
  567.             return name;
  568.  
  569.         table = archive.tableForId[id];
  570.         if (table == null) {
  571.             return null;
  572.         }
  573.  
  574.         // Create base name for all the instances from this table.
  575.  
  576.         base = (String)baseNameForTable.get(table);
  577.         if (base == null) {
  578.             base = table.className();
  579.             lastDotIndex = base.lastIndexOf('.');
  580.             if (lastDotIndex > 0 && lastDotIndex < (base.length() - 1)) {
  581.                 if (base.charAt(0) != '[')
  582.                     base = base.substring(lastDotIndex + 1, base.length());
  583.             }
  584.  
  585.             // Append a character which can't appear in a valid class name
  586.             // to ensure uniqueness.
  587.  
  588.             base = base + "-";
  589.  
  590.             // Test to make sure this base is unique.  If the short name is
  591.             // not unique, then fall back to the long name.
  592.  
  593.             if (baseNameForTable.get(base) != null) {
  594.                 base = table.className() + "-";
  595.  
  596.                 // If the long name isn't unique, then just keep trying.
  597.                 digits = 0;
  598.                 while (baseNameForTable.get(base) != null) {
  599.                     base = table.className() + "-" + digits + "-";
  600.                     digits++;
  601.                 }
  602.             }
  603.  
  604.             baseNameForTable.put(table, base);
  605.         }
  606.  
  607.         // For now just make a name that is the class name followed by a
  608.         // unique number (the row number in that table).
  609.  
  610.         digits = decimalDigitCount(table.rowCount());
  611.         seqString = decimalString(archive.rowForId[id], digits);
  612.  
  613.         name = base + seqString;
  614.         nameForId[id] = name;
  615.  
  616.         return name;
  617.     }
  618.  
  619.     int decimalDigitCount(int number) {
  620.         int i = 0;
  621.  
  622.         while (number > 0) {
  623.             i++;
  624.             number = number / 10;
  625.         }
  626.  
  627.         return i;
  628.     }
  629.  
  630.     String decimalString(int number, int digits) {
  631.         int i;
  632.         char buf[] = new char[digits];
  633.  
  634.         for (i = 0; i < digits; i++) {
  635.             buf[i] = '0';
  636.         }
  637.  
  638.         i = digits;
  639.         while (number > 0 && i > 0) {
  640.             i--;
  641.             buf[i] = (char)(number % 10 + '0');
  642.             number = number / 10;
  643.         }
  644.  
  645.         return new String(buf);
  646.     }
  647.  
  648.     void saveClassInfo() {
  649.         Hashtable allTablesDict, tableDict;
  650.         Enumeration tableEnum;
  651.         ClassTable table;
  652.  
  653.         allTablesDict = new Hashtable();
  654.         tableEnum = archive.classTables.elements();
  655.  
  656.         while (tableEnum.hasMoreElements()) {
  657.             table = (ClassTable)tableEnum.nextElement();
  658.             tableDict = dictionaryForTable(table);
  659.             allTablesDict.put(table.className(), tableDict);
  660.         }
  661.  
  662.         archiveDict.put(classTablesKey, allTablesDict);
  663.     }
  664.  
  665.     Hashtable dictionaryForTable(ClassTable table) {
  666.         int i;
  667.         Hashtable tableDict;
  668.         String typeArray[], classVersions[];
  669.  
  670.         tableDict = new Hashtable(5);
  671.         typeArray = new String[table.fieldCount];
  672.  
  673.         for (i = 0; i < table.fieldCount; i++) {
  674.             typeArray[i] = nameForType(table.fieldTypes[i]);
  675.         }
  676.  
  677.         tableDict.put(fieldNamesKey, table.fieldNames);
  678.         tableDict.put(fieldTypesKey, typeArray);
  679.  
  680.         classVersions = new String[table.classCount];
  681.  
  682.         for (i = 0; i < table.classCount; i++) {
  683.             classVersions[i] = String.valueOf(table.classVersions[i]);
  684.         }
  685.  
  686.         tableDict.put(classNamesKey, table.classNames);
  687.         tableDict.put(classVersionsKey, classVersions);
  688.  
  689.         return tableDict;
  690.     }
  691.  
  692.     String nameForType(int fieldType) {
  693.         switch (fieldType) {
  694.             case Codable.BOOLEAN_TYPE:       return "boolean";
  695.             case Codable.BOOLEAN_ARRAY_TYPE: return "boolean[]";
  696.             case Codable.CHAR_TYPE:          return "char";
  697.             case Codable.CHAR_ARRAY_TYPE:    return "char[]";
  698.             case Codable.BYTE_TYPE:          return "byte";
  699.             case Codable.BYTE_ARRAY_TYPE:    return "byte[]";
  700.             case Codable.SHORT_TYPE:         return "short";
  701.             case Codable.SHORT_ARRAY_TYPE:   return "short[]";
  702.             case Codable.INT_TYPE:           return "int";
  703.             case Codable.INT_ARRAY_TYPE:     return "int[]";
  704.             case Codable.LONG_TYPE:          return "long";
  705.             case Codable.LONG_ARRAY_TYPE:    return "long[]";
  706.             case Codable.FLOAT_TYPE:         return "float";
  707.             case Codable.FLOAT_ARRAY_TYPE:   return "float[]";
  708.             case Codable.DOUBLE_TYPE:        return "double";
  709.             case Codable.DOUBLE_ARRAY_TYPE:  return "double[]";
  710.             case Codable.STRING_TYPE:        return "java.lang.String";
  711.             case Codable.STRING_ARRAY_TYPE:  return "java.lang.String[]";
  712.             case Codable.OBJECT_TYPE:        return "java.lang.Object";
  713.             case Codable.OBJECT_ARRAY_TYPE:  return "java.lang.Object[]";
  714.             default:
  715.                 throw new IllegalArgumentException("Unknown field type: " +
  716.                     fieldType);
  717.         }
  718.     }
  719.  
  720.     void saveInstanceData() {
  721.         int i, row;
  722.         Hashtable allInstancesDict, instanceDict, allTablesDict, tableDict;
  723.         ClassTable table;
  724.         String name;
  725.  
  726.         allTablesDict = (Hashtable)archiveDict.get(classTablesKey);
  727.  
  728.         // id = 0 means null so we can just skip it.
  729.  
  730.         for (i = 1; i < archive.idCount; i++) {
  731.             table = archive.tableForId[i];
  732.             row = archive.rowForId[i];
  733.             instanceDict = dictionaryForInstance(table, row);
  734.  
  735.             tableDict = (Hashtable)allTablesDict.get(table.className);
  736.             allInstancesDict = (Hashtable)tableDict.get(instancesKey);
  737.             if (allInstancesDict == null) {
  738.                 allInstancesDict = new Hashtable();
  739.                 tableDict.put(instancesKey, allInstancesDict);
  740.             }
  741.  
  742.             allInstancesDict.put(nameForId(i), instanceDict);
  743.         }
  744.     }
  745.  
  746.     Hashtable dictionaryForInstance(ClassTable table, int row) {
  747.         int i;
  748.         Hashtable dict;
  749.         Object fieldValue;
  750.  
  751.         dict = new Hashtable(2 * table.fieldCount);
  752.  
  753.         for (i = 0; i < table.fieldCount; i++) {
  754.             fieldValue = valueForField(table, row, i);
  755.             if (fieldValue != null)
  756.                 dict.put(table.fieldNames[i], fieldValue);
  757.         }
  758.  
  759.         return dict;
  760.     }
  761.  
  762.     Object valueForField(ClassTable table, int row, int column) {
  763.         int i, count;
  764.         boolean booleanArray[];
  765.         char charArray[];
  766.         byte byteArray[];
  767.         short shortArray[];
  768.         int intArray[];
  769.         long longArray[];
  770.         float floatArray[];
  771.         double doubleArray[];
  772.         String stringArray[];
  773.  
  774.         switch (table.fieldTypes[column]) {
  775.             case Codable.BOOLEAN_TYPE:
  776.                 return String.valueOf(table.booleanAt(row, column));
  777.             case Codable.BOOLEAN_ARRAY_TYPE:
  778.                 booleanArray = table.booleanArrayAt(row, column);
  779.                 if (booleanArray == null)
  780.                     return null;
  781.                 count = booleanArray.length;
  782.                 stringArray = new String[count];
  783.                 for (i = 0; i < count; i++)
  784.                     stringArray[i] = String.valueOf(booleanArray[i]);
  785.                 return stringArray;
  786.             case Codable.CHAR_TYPE:
  787.                 return String.valueOf(table.charAt(row, column));
  788.             case Codable.CHAR_ARRAY_TYPE:
  789.                 charArray = table.charArrayAt(row, column);
  790.                 if (charArray == null)
  791.                     return null;
  792.                 count = charArray.length;
  793.                 stringArray = new String[count];
  794.                 for (i = 0; i < count; i++)
  795.                     stringArray[i] = String.valueOf(charArray[i]);
  796.                 return stringArray;
  797.             case Codable.BYTE_TYPE:
  798.                 return String.valueOf(table.byteAt(row, column) & 0xff);
  799.             case Codable.BYTE_ARRAY_TYPE:
  800.                 return bytesString(table.byteArrayAt(row, column));
  801.             case Codable.SHORT_TYPE:
  802.                 return String.valueOf(table.shortAt(row, column));
  803.             case Codable.SHORT_ARRAY_TYPE:
  804.                 shortArray = table.shortArrayAt(row, column);
  805.                 if (shortArray == null)
  806.                     return null;
  807.                 count = shortArray.length;
  808.                 stringArray = new String[count];
  809.                 for (i = 0; i < count; i++)
  810.                     stringArray[i] = String.valueOf(shortArray[i]);
  811.                 return stringArray;
  812.             case Codable.INT_TYPE:
  813.                 return String.valueOf(table.intAt(row, column));
  814.             case Codable.INT_ARRAY_TYPE:
  815.                 intArray = table.intArrayAt(row, column);
  816.                 if (intArray == null)
  817.                     return null;
  818.                 count = intArray.length;
  819.                 stringArray = new String[count];
  820.                 for (i = 0; i < count; i++)
  821.                     stringArray[i] = String.valueOf(intArray[i]);
  822.                 return stringArray;
  823.             case Codable.LONG_TYPE:
  824.                 return String.valueOf(table.longAt(row, column));
  825.             case Codable.LONG_ARRAY_TYPE:
  826.                 longArray = table.longArrayAt(row, column);
  827.                 if (longArray == null)
  828.                     return null;
  829.                 count = longArray.length;
  830.                 stringArray = new String[count];
  831.                 for (i = 0; i < count; i++)
  832.                     stringArray[i] = String.valueOf(longArray[i]);
  833.                 return stringArray;
  834.             case Codable.FLOAT_TYPE:
  835.                 return String.valueOf(table.floatAt(row, column));
  836.             case Codable.FLOAT_ARRAY_TYPE:
  837.                 floatArray = table.floatArrayAt(row, column);
  838.                 if (floatArray == null)
  839.                     return null;
  840.                 count = floatArray.length;
  841.                 stringArray = new String[count];
  842.                 for (i = 0; i < count; i++)
  843.                     stringArray[i] = String.valueOf(floatArray[i]);
  844.                 return stringArray;
  845.             case Codable.DOUBLE_TYPE:
  846.                 return String.valueOf(table.doubleAt(row, column));
  847.             case Codable.DOUBLE_ARRAY_TYPE:
  848.                 doubleArray = table.doubleArrayAt(row, column);
  849.                 if (doubleArray == null)
  850.                     return null;
  851.                 count = doubleArray.length;
  852.                 stringArray = new String[count];
  853.                 for (i = 0; i < count; i++)
  854.                     stringArray[i] = String.valueOf(doubleArray[i]);
  855.                 return stringArray;
  856.             case Codable.STRING_TYPE:
  857.                 return table.stringAt(row, column);
  858.             case Codable.STRING_ARRAY_TYPE:
  859.                 return table.stringArrayAt(row, column);
  860.             case Codable.OBJECT_TYPE:
  861.                 return nameForId(table.identifierAt(row, column));
  862.             case Codable.OBJECT_ARRAY_TYPE:
  863.                 intArray = table.identifierArrayAt(row, column);
  864.                 if (intArray == null)
  865.                     return null;
  866.                 count = intArray.length;
  867.                 stringArray = new String[count];
  868.                 for (i = 0; i < count; i++)
  869.                     stringArray[i] = nameForId(intArray[i]);
  870.                 return stringArray;
  871.             default:
  872.                 throw new InconsistencyException("Unknown field type: " +
  873.                     table.fieldTypes[column]);
  874.         }
  875.     }
  876.  
  877.     static byte hexChar[] = {
  878.         '0', '1', '2', '3', '4', '5', '6', '7',
  879.         '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
  880.     };
  881.  
  882.     static String bytesString(byte bytes[]) {
  883.         int i, count, outCount;
  884.         byte b;
  885.         byte buf[];
  886.  
  887.         if (bytes == null)
  888.             return null;
  889.  
  890.         if (bytes.length == 0)
  891.             return "";
  892.  
  893.         count = bytes.length;
  894.         buf = new byte[2 * count + (count / 4) + 1];
  895.         outCount = 0;
  896.  
  897.         for (i = 0; i < count; i++) {
  898.             b = bytes[i];
  899.             buf[outCount++] = hexChar[(b >>> 4) & 0xf];
  900.             buf[outCount++] = hexChar[b & 0xf];
  901.             if (((i + 1) % 4) == 0)
  902.                 buf[outCount++] = ' ';
  903.         }
  904.  
  905.         if (buf[outCount - 1] == ' ')
  906.             outCount--;
  907.  
  908.         return new String(buf, 0, 0, outCount);
  909.     }
  910.  
  911.     void saveRoots() {
  912.         int i;
  913.         String rootsArray[];
  914.  
  915.         rootsArray = new String[archive.rootCount];
  916.  
  917.         for (i = 0; i < archive.rootCount; i++)
  918.             rootsArray[i] = nameForId(archive.roots[i]);
  919.  
  920.         archiveDict.put(rootInstancesKey, rootsArray);
  921.     }
  922. }
  923.